home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / VOL_400 / 466_01 / SRC / FMT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-20  |  18.5 KB  |  819 lines

  1. #include <afx.h>
  2. #include <afxtempl.h>
  3.  
  4. #include "parse.h"
  5. #include "input.h"
  6. #include "fmt.h"
  7. #include "errmsg.h"
  8.  
  9. CString sEmpty;
  10.  
  11. //@doc 
  12.  
  13. //@module FMT.CPP | This module contains the format list, format input
  14. // file class, and base class for all format tag objects.
  15. //
  16. // The format list parser, embodied in <mf CFmtList::GetSection>, calls into
  17. // the individual format base classes <mf CFmtBase::ParseEntry> to create
  18. // the appropriate object types and parse the entries.
  19. //
  20. // For searching the format file, each format tag class derived from 
  21. // <c CFmtBase> overrides the <mf CFmtBase::Match> function. These overridden
  22. // functions are called by <mf CFmtList::Get>; the objects are compared
  23. // against the search query objects derived from <c CFmtSrch>.
  24. // 
  25. //@head3 Format Input  |
  26. //@index | FMT & INPUT
  27. //@head3 Format Tag   | 
  28. //@index | FMT & ENTRYBASE
  29. //@head3 Format List   |
  30. //@index | FMT & ENTRYLIST
  31. //@head3 Format Search  |
  32. //@index | FMT & SRCHBASE
  33. //@head3 Generic Stuff   |
  34. //@index | FMT & GENERIC
  35.  
  36.  
  37.  
  38.  
  39. /**************************************************************************/
  40. //@doc FMT GENERIC
  41.  
  42. //@func Copies a string from source to destination, stripping
  43. // extra whitespace out of the source string (destination string
  44. // contains each token followed by a single space, including the
  45. // last token). Also normalizes to all uppercase.
  46. //
  47. // @comm Destination string should be same length as source string.
  48.  
  49. void CopyFlags(
  50.     CString &sDest,            //@parm Destination string
  51.     const char *szSrc)        //@parm Source string
  52. {
  53.     const char *szEnd;
  54.     static char szFlagStopChar[] = " \t\r\n*";
  55.     static char szFlagEolChar[] = "\r\n*";
  56.     char szToken[MAXTAGSIZE+1];
  57.  
  58.     ASSERT(szSrc);
  59.  
  60.     sDest.Empty();
  61.  
  62.     for(szSrc = EatWhite(szSrc); 
  63.         *szSrc && strchr(szFlagEolChar, *szSrc) == 0; 
  64.         szSrc = EatWhite(szEnd))
  65.     {
  66.         szEnd = strpbrk(szSrc, szFlagStopChar);
  67.  
  68.         if(NULL == szEnd)
  69.             szEnd = szSrc+strlen(szSrc);
  70.  
  71.         if(szEnd-szSrc > MAXTAGSIZE)
  72.             szEnd = szSrc+MAXTAGSIZE;
  73.  
  74.         memcpy(szToken, szSrc, szEnd-szSrc);
  75.         szToken[szEnd-szSrc] = '\0';
  76.  
  77.         sDest += chSpace;
  78.         sDest += szToken;
  79.     }
  80.  
  81.     sDest += chSpace;
  82.     sDest.MakeUpper();
  83. }
  84.  
  85.  
  86. /**************************************************************************/
  87.  
  88. //@doc FMT INPUT
  89.  
  90. //@mfunc Constructor, zeroes the state variables and filename.
  91. CFmtInput::CFmtInput(void)
  92. {
  93.     m_nTokens = 0;
  94.     ZeroMem(&m_nAdState, sizeof(m_nAdState));
  95.     ZeroMem(m_szFilename, _MAX_PATH+1);
  96. }
  97.  
  98. //@mfunc Breaks the input line into a series of comma-separated
  99. // tokens. Sets fields of <md .m_aszTokens> to the starting points of
  100. // the fields, and updates <md .m_nTokens>. Each field is null-terminated.
  101.  
  102. void CFmtInput::Tokenize(
  103.     char *szSrc)        //@parm Optional point at which to start tokenizing;
  104. {                       // if omitted, tokenizes <md .m_szLineBuf>.
  105.     char *szToken;
  106.  
  107.     static char szFieldTerm[] = ";,\r\n";
  108.     static char szLineTerm[]  = ";\r\n";
  109.  
  110.     if(szSrc == NULL)
  111.         szSrc = m_szLineBuf;
  112.  
  113.     ASSERT(szSrc);
  114.  
  115.     ZeroMem(m_aszTokens, sizeof(char *)*MAXFMTTOKENS);
  116.     
  117.     int fContinue = TRUE;
  118.  
  119.     for(m_nTokens = 0; fContinue && m_nTokens < MAXFMTTOKENS; szSrc++, m_nTokens++)
  120.     {
  121.         for(szToken = szSrc; *szSrc && !strchr(szFieldTerm, *szSrc); szSrc++);
  122.  
  123.         m_aszTokens[m_nTokens] = szToken;
  124.  
  125.         if(*szSrc == '\0' || strchr(szLineTerm, *szSrc))
  126.             fContinue = FALSE;
  127.         
  128.         *szSrc = '\0';
  129.     }
  130. }
  131.  
  132.  
  133. /**************************************************************************/
  134.  
  135. //@mfunc Opens a format file, using the <mf .GetPath> function to 
  136. // search for the file.
  137. //
  138. //@rdesc Zero or an error code.
  139.  
  140. int CFmtInput::OpenFmtFile(
  141.     const char *szName,         //@parm Format filename
  142.     const char *szAutoduckPath)  //@parm Autoduck.Exe path name.
  143. {
  144. TRY
  145. {
  146.     int nRet;
  147.     CFileException ex;
  148.  
  149.     ASSERT(m_szFilename);
  150.  
  151.     nRet = GetPath(szName, szAutoduckPath);
  152.     if(nRet)
  153.         return nRet;
  154.  
  155.     if(Open(m_szFilename, typeBinary | modeRead, &ex) == FALSE)
  156.         return ex.m_cause;
  157.  
  158.     return 0;
  159. }
  160. CATCH(CFileException, e)
  161. {
  162.     return e->m_cause;
  163. }
  164. END_CATCH
  165. }
  166.  
  167.  
  168. //@mfunc Finds the specified file and copies a fully qualified path
  169. // name to the <p szFullPath> buffer.
  170. //
  171. // Searches: current directory; then the directory in which AUTODUCK.EXE
  172. // is located; then the search path. If a drive letter or subdirectory
  173. // path is specified, it just looks there.
  174. //
  175. //@rdesc Zero if the specfied file was found and 1 if no file was found.
  176.  
  177.  
  178. int CFmtInput::GetPath(
  179.     const char *szTarget,             //@parm Name of format file to find.
  180.     const char *szAutoduckPath)        //@parm Location of AUTODUCK.EXE
  181. {                                
  182.     CFileStatus status;
  183.     char szPath[_MAX_PATH];
  184.  
  185.  
  186.     ASSERT(m_szFilename != NULL);
  187.  
  188.     // First look in the current directory.
  189.  
  190.     if(GetStatus(szTarget, status))
  191.     {
  192.         strcpy(m_szFilename, szTarget);
  193.         return 0;
  194.     }
  195.     
  196.     // If file not found, and a subdir path or drive letter
  197.     // was specified, exit now.
  198.  
  199.     if(*szTarget == chBackslash || *(szTarget+1) == chColon ||
  200.        *szTarget == chPeriod)
  201.     {
  202.         return errfileFileNotFound;
  203.     }
  204.  
  205.     // Now look in the same directory as AUTODUCK.EXE
  206.         
  207.     ASSERT(szAutoduckPath);
  208.     _splitpath(szAutoduckPath, m_szFilename, szPath, NULL, NULL);
  209.     strcat(m_szFilename, szPath);
  210.     strcat(m_szFilename, szTarget);
  211.  
  212.     if(GetStatus(m_szFilename, status))
  213.         return 0;
  214.  
  215.     // Finally, search the path.
  216.  
  217.     _searchenv(szTarget, "PATH", m_szFilename);
  218.     if(GetStatus(m_szFilename, status))
  219.         return 0;
  220.  
  221.     return errfileFileNotFound;
  222. }
  223.  
  224.  
  225. /***********************************************************/
  226.  
  227. //@mfunc Set the output type for the extract operation.
  228.  
  229. void CFmtInput::SetOutputType(
  230.     const char *szOutputType)   //@parm Output type.
  231. {
  232.     ASSERT(szOutputType);
  233.  
  234.     m_sOutputType = szOutputType;
  235. }
  236.  
  237.  
  238.  
  239.  
  240. /**************************************************************************/
  241.  
  242. //@doc FMT ENTRYBASE
  243.  
  244. //@mfunc Constructs a format base class.
  245.  
  246. CFmtBase::CFmtBase(
  247.     int nFmtStrings)    //@parm How many format strings in the class
  248. {
  249.     m_aFmtStrings.SetSize(nFmtStrings);
  250.  
  251.     m_nInput = 0;
  252.     m_lSrcLine = 0;
  253. }
  254.  
  255.  
  256. //@mfunc Destructs a format base class. Deletes all format strings.
  257.  
  258. CFmtBase::~CFmtBase(void)
  259. {
  260.     for(int i = 0; i < m_aFmtStrings.GetSize(); i++)
  261.         if(m_aFmtStrings[i])
  262.             delete m_aFmtStrings[i];
  263. }
  264.  
  265.  
  266.  
  267. /***********************************************************/
  268. // Format string access
  269. /***********************************************************/
  270.  
  271. //@mfunc Retrieves a format string. Always returns a valid
  272. // <c CString> object (empty if format string not specified
  273. // in format file.
  274. //
  275. //@rdesc Reference to the specified format string. If <p nFmt>
  276. // is out of range an empty string is returned.
  277.  
  278. CString &CFmtBase::GetFmtString(
  279.     int nFmt)                   //@parm Which format string to retrieve
  280. {
  281.     ASSERT(nFmt < m_aFmtStrings.GetSize());
  282.  
  283.     if(nFmt >= m_aFmtStrings.GetSize())
  284.         return sEmpty;
  285.  
  286.     if(NULL == m_aFmtStrings[nFmt])
  287.         return sEmpty;
  288.     else
  289.         return *m_aFmtStrings[nFmt];
  290. }
  291.  
  292.  
  293.  
  294. #ifdef _DEBUG
  295.  
  296. //@func Dumps a format string.
  297.  
  298. void DumpFmt(
  299.     CDumpContext &dc,   //@parm Dump context.
  300.     const char *szBuf)  //@parm Format string to dump.
  301. {
  302.     int nLen = strlen(szBuf);
  303.     int nRight, i;
  304.     char szChar[2];
  305.     
  306.     szChar[0] = szChar[1] = '\0';
  307.  
  308.     for(i = 0; i < 20 && i < nLen; i++)
  309.     {
  310.         switch(szBuf[i])
  311.         {
  312.         case '\r':
  313.             dc << "^r";
  314.             break;
  315.         case '\n':
  316.             dc << "^n";
  317.             break;
  318.         case '\t':
  319.             dc << "^t";
  320.             break;
  321.         default:
  322.             szChar[0] = szBuf[i];
  323.             dc << szChar;
  324.             break;
  325.         }
  326.     }
  327.  
  328.     
  329.     dc << " ... ";
  330.  
  331.     if(nLen > 20)
  332.     {
  333.         nRight = max(nLen-20, 20);
  334.  
  335.         for(i = nRight; i < nLen; i++)
  336.         {
  337.             switch(szBuf[i])
  338.             {
  339.             case '\r':
  340.                 dc << "^r";
  341.                 break;
  342.             case '\n':
  343.                 dc << "^n";
  344.                 break;
  345.             case '\t':
  346.                 dc << "^t";
  347.                 break;
  348.             default:
  349.                 szChar[0] = szBuf[i];
  350.                 dc << szChar;
  351.                 break;
  352.             }
  353.         }
  354.     }
  355. }
  356.  
  357. //@mfunc Dumps a format object.
  358.  
  359. void CFmtBase::Dump(
  360.     CDumpContext &dc) const     //@parm Dump context.
  361. {
  362.     int nFmtStrings = m_aFmtStrings.GetSize();
  363.  
  364.     dc << "CFmtBase: " << nFmtStrings << " strings:\r\n";
  365.     
  366.     for(int n = 0; n < nFmtStrings; n++)
  367.     {
  368.         dc  << "\t[" << n << "]: ";
  369.  
  370.         if(m_aFmtStrings[n])
  371.             DumpFmt(dc, *m_aFmtStrings[n]);
  372.         else
  373.             dc << "NULL";
  374.  
  375.         dc << "\n";
  376.     }
  377. }
  378. #endif
  379.  
  380. /**************************************************************************/
  381.  
  382.  
  383. // ***********************
  384. // Ctor
  385. // ***********************
  386.  
  387. //@doc FMT ENTRYLIST
  388.  
  389.  
  390. //@mfunc Format list class. This object stores a homogenous 
  391. // list of formatting objects.
  392.  
  393. CFmtList::CFmtList(void)
  394. {
  395.     m_pNew = NULL;
  396.  
  397.     m_lPosStart = -1;
  398.     m_lPosCur = -1;
  399.     m_lPosNext = -1;
  400.  
  401.     m_nTag = -1;
  402.  
  403.     m_nState.Tag   = 0; 
  404.     m_nState.Multi = 0; 
  405.     m_nState.Done  = 0; 
  406.     m_nState.Skip  = 0;
  407. }
  408.  
  409.  
  410. //@mfunc Destructs the list.
  411.  
  412. CFmtList::~CFmtList(void)
  413. {
  414.     POSITION pos;
  415.     CFmtBase *p;
  416.  
  417.     //Iterate through the list in head-to-tail order.
  418.     for( pos = m_listTags.GetHeadPosition(); pos != NULL; )
  419.     {
  420.         p = m_listTags.GetNext( pos );
  421.     
  422.         delete p;
  423.     }
  424. }
  425.  
  426.  
  427. #ifdef _DEBUG
  428.  
  429. //@mfunc Dumps the format list.
  430.  
  431. void CFmtList::Dump( 
  432.     CDumpContext &dc ) const        //@parm Where to dump to
  433. {
  434.      dc << "CFmtList -- Count = " << m_listTags.GetCount() << "\r\n";
  435.     dc << m_listTags << "\r\n";
  436. }
  437. #endif
  438.  
  439.  
  440.  
  441. // ***********************
  442. // Section parsing functions
  443. // ***********************
  444.  
  445. //@mfunc Parses a format file section. The input file state <p in>
  446. // is left at the next format file section or EOF (or at the position
  447. // of the parsing error).
  448. //
  449. //@rdesc Zero if parsing successful or an error code if a parsing
  450. // error occurred.
  451.  
  452. int CFmtList::GetSection(
  453.     CFmtInput &in)          //@parm Input state.
  454. {
  455. TRY
  456. {
  457.     int  nRet;                              // Return values
  458.  
  459.     const char *szT;                              // Temp
  460.     
  461.     m_nState.Tag   = 0; 
  462.     m_nState.Multi = 0; 
  463.     m_nState.Done  = 0; 
  464.     m_nState.Skip  = 0;
  465.  
  466.     m_nTag         = 0;
  467.     m_lPosNext     = in.GetCurPos();
  468.     m_lPosStart = 0;
  469.  
  470.     while(!(m_nState.Done))
  471.     {
  472.         // Remember position of next line to read.
  473.  
  474.            m_lPosCur = m_lPosNext;
  475.         
  476.         // Get the next input line, storing the current & next line positions
  477.         
  478.         nRet = in.GetLine();
  479.         if(nRet)
  480.             return nRet;
  481.         
  482.         // If at EOF we will finish on this line.
  483.  
  484.         if(in.EndOfFile())
  485.             m_nState.Done = TRUE;
  486.  
  487.         // Get the position of the next line.
  488.  
  489.         m_lPosNext = in.GetCurPos();
  490.  
  491.         // If in the process of adding multiline text, see if this line ends
  492.         // the text block. If so, read the text into memory.
  493.         
  494.         if(m_nState.Multi)
  495.         {
  496.             nRet = CheckFmtString(in);
  497.             if(nRet)
  498.                 return nRet;
  499.                 
  500.             // If we're still in add-mode, skip this line (it's just free-form
  501.             // text belonging to a previous tag).
  502.             
  503.             if(m_nState.Multi)
  504.                 continue;
  505.         }
  506.         
  507.         // Now start parsing the entry line.
  508.         
  509.         szT = in.m_szLineBuf; 
  510.  
  511.         switch(*szT)
  512.         {
  513.         case chFmtSectOpen:            // End of section?
  514.             m_nState.Done = TRUE;
  515.             in.SetReuse();
  516.             break;
  517.         
  518.         case chFmtTagOpen:         // Tag? Get the tag, and process it...
  519.             
  520.             szT = GetFmtTag(in);
  521.             if(szT == NULL)
  522.                 return fmterrBadFmtEntry;
  523.  
  524.             in.Tokenize(in.m_szLineBuf+(szT-in.m_szLineBuf));
  525.  
  526.             nRet = ParseEntry(in);
  527.             if(nRet)
  528.                 return nRet;
  529.         
  530.             break;
  531.  
  532.         default:                    // Any other line must be empty or a comment
  533.             szT = EatWhite(szT);
  534.             if(*szT == '\0' || *szT == chFmtComment)
  535.                 break;
  536.             else
  537.                 return fmterrExpectedEol;
  538.         }
  539.     }
  540.     
  541.     nRet = CheckAddTag();
  542.     if(nRet)
  543.         return nRet;
  544.  
  545.     return Validate();
  546. }
  547. CATCH(CMemoryException, e)
  548. {
  549.     return errMemory;
  550. }
  551. END_CATCH
  552. }
  553.  
  554.  
  555.  
  556. //@mfunc Called when a new tag is encountered in the format section,
  557. // this function adds the current tag to the list.
  558. //
  559. //@rdesc Zero or an error code if the current tag is missing some
  560. // pieces.
  561.  
  562. int CFmtList::CheckAddTag(void)
  563. {
  564.     int nRet;
  565.         
  566.     if(m_pNew)
  567.     {
  568.         nRet = m_pNew->Validate();
  569.         if(nRet)
  570.             return nRet;
  571.         
  572.         m_listTags.AddTail(m_pNew);
  573.         m_pNew = NULL;
  574.     }
  575.  
  576.     return 0;
  577. }
  578.  
  579.  
  580. //@mfunc Checks the input state to determine whether the current
  581. // format string should be terminated and added to the tag.
  582. //
  583. //@rdesc Zero on success or an error code if a memory or file
  584. // exception occurs.
  585.  
  586. int CFmtList::CheckFmtString(
  587.     CFmtInput &in)                //@parm Input state structure
  588. {
  589. TRY
  590. {
  591.     DWORD dwCharsRead;
  592.     DWORD dwToRead;
  593.  
  594.     if(m_nState.Done || 
  595.        in.m_szLineBuf[0] == chFmtSectOpen ||
  596.        in.m_szLineBuf[0] == chFmtTagOpen  ||
  597.        in.m_szLineBuf[0] == chFmtComment  ||
  598.        in.m_szLineBuf[0] == '\0')
  599.     {
  600.         m_nState.Multi = FALSE;
  601.  
  602.         if(m_nState.Skip)
  603.             return 0;            
  604.  
  605.         ASSERT(m_pNew);
  606.         ASSERT(m_nTag <= m_pNew->m_aFmtStrings.GetSize());
  607.  
  608.         dwToRead = m_lPosCur - m_lPosStart - 2;
  609.  
  610.         ASSERT(dwToRead >= 0);
  611.         ASSERT(m_pNew->m_aFmtStrings[m_nTag] == NULL);
  612.  
  613.         m_pNew->m_aFmtStrings[m_nTag] = new CString;
  614.  
  615.         if(dwToRead > 0)
  616.         {
  617.             in.Seek(m_lPosStart, CFile::begin);
  618.  
  619.             LPTSTR sz = m_pNew->m_aFmtStrings[m_nTag]->GetBufferSetLength(dwToRead+1);
  620.     
  621.             dwCharsRead = in.ReadHuge(sz, dwToRead);
  622.     
  623.             sz[dwCharsRead] = '\0';
  624.  
  625.             m_pNew->m_aFmtStrings[m_nTag]->ReleaseBuffer();
  626.  
  627.             in.Seek(m_lPosNext, CFile::begin);
  628.         }
  629.     }
  630.  
  631.     return 0;
  632. }
  633. CATCH(CFileException, e)
  634. {
  635.     return e->m_cause;
  636. }
  637. AND_CATCH(CMemoryException, e)
  638. {
  639.     return errMemory;
  640. }
  641. END_CATCH
  642. }
  643.  
  644.  
  645.  
  646. //@mfunc Gets the index of the current format tag. Calls the <mf .FmtTagList>
  647. // structure to obtain the list of tags for the child format object.
  648. //
  649. //@rdesc Returns a pointer to the first character following the tag name.
  650.  
  651. const char *CFmtList::GetFmtTag(
  652.     CFmtInput &in)                 //@parm Format input state.
  653. {
  654.     const char *szEnd = SeekEnd(in.m_szLineBuf+1, " =", MAXTAGSIZE);
  655.     if(szEnd == NULL)
  656.         return NULL;
  657.  
  658.     CString sTag(in.m_szLineBuf+1, szEnd-(in.m_szLineBuf+1));
  659.  
  660.     int i;
  661.  
  662.     TAGSPEC *aTagList = FmtTagList();
  663.     
  664.     szEnd = EatWhite(szEnd);
  665.     if(*szEnd != chFmtDefSep)
  666.         return NULL;
  667.         
  668.     for(i = 0; aTagList[i].iTag != -1; i++)
  669.     {
  670.         if(_stricmp(sTag, aTagList[i].szName) == 0)
  671.         {
  672.             m_nTag = aTagList[i].iTag;
  673.             return ++szEnd;
  674.         }
  675.     }
  676.     return NULL;
  677. }
  678.  
  679.  
  680.  
  681. /**************************************************************************/
  682.  
  683. //@mfunc Adds the format object to the list.
  684. //
  685. //@rdesc Zero or errMemory if a memory exception occurs.
  686.  
  687. int CFmtList::Add(
  688.     CFmtBase *p)                //@parm Format object to add
  689. {
  690. TRY
  691. {
  692.     ASSERT(p);
  693.  
  694.     m_listTags.AddTail(p);
  695.  
  696.     return 0;
  697. }
  698. CATCH(CMemoryException, e)
  699. {
  700.     return errMemory;
  701. }
  702. END_CATCH
  703. }
  704.  
  705.  
  706. /**************************************************************************/
  707.  
  708. // Tag retrieval
  709.  
  710. //@mfunc Retrieves a formatting object from the list, given
  711. // a format-file search query. The function calls the <mf CFmtBase::Match>
  712. // function, which is overridden in child classes to perform section-specific
  713. // comparisons. 
  714. //
  715. //@rdesc Pointer to the matching format object, or NULL if no object matches.
  716.  
  717. CFmtBase *CFmtList::Get(
  718.     CFmtSrchBase *psrch)    //@parm Search object. Child objects cast this 
  719. {                           // to the appropriate search object type.
  720.     POSITION pos;
  721.     CFmtBase *p;
  722.  
  723.     pos = m_listTags.GetHeadPosition();
  724.  
  725.     while(pos != NULL)
  726.     {
  727.         p = m_listTags.GetNext(pos);
  728.  
  729.         if(p->Match(psrch))
  730.             return p;
  731.     }
  732.  
  733.     return NULL;
  734. }
  735.  
  736. /**************************************************************************/
  737.  
  738. // Helper functions for derived classes
  739.  
  740. //@mfunc Sets the parser up to accept the current line as a format 
  741. // string. Sets state variables and performs standard validations on
  742. // the input state.
  743. //
  744. //@rdesc Zero if successful or an error code if a parsing error occured.
  745.  
  746. int CFmtList::SetFmtString(
  747.     CFmtInput &in,          //@parm INput state
  748.     int nField)             //@parm Which token has the format string
  749. {
  750.     m_nState.Multi = TRUE;
  751.  
  752.     if(m_nState.Skip)
  753.         return 0;
  754.  
  755.     if(!m_nState.Tag)
  756.         return fmterrOrphanedTag;
  757.  
  758.     ASSERT(m_pNew);
  759.     ASSERT(nField < in.m_nTokens);
  760.  
  761.     if(m_pNew->m_aFmtStrings[m_nTag])
  762.         return fmterrDuplicateEntry;
  763.  
  764.     m_lPosStart = m_lPosCur + 
  765.         (in.m_aszTokens[nField] - in.m_szLineBuf);
  766.  
  767.     ASSERT(m_lPosStart > 0 && m_lPosStart > m_lPosCur);
  768.  
  769.     return 0;
  770. }
  771.  
  772.  
  773. // ***********************
  774. // Output Type functions
  775. // ***********************
  776.  
  777. //@mfunc Checks an output type string in a tag definition against the
  778. // output type stored in <md CFmtInput.m_sOutputType>. Sets state variables
  779. // in <p in> to read or skip tag definitions depending on whether the output
  780. // types match.
  781. //
  782. //@rdesc TRUE if in tag state or FALSE if output type does not match.
  783.  
  784. int CFmtList::CheckOutputType(
  785.     CFmtInput &in,              //@parm Input state
  786.     int  nTagField)             //@parm Which tag field token has the output
  787. {                               // type.
  788.  
  789.     ASSERT(!in.m_sOutputType.IsEmpty());
  790.  
  791.     const char *szStart = EatWhite(in.m_aszTokens[nTagField]);
  792.     const char *szEnd   = TrimWhite(szStart+strlen(szStart), szStart);
  793.  
  794.     CString sOutputType(szStart, szEnd-szStart+1);
  795.  
  796.     // If output type of tag is within set that we are considering,
  797.     // retain it; otherwise, we will discard it and all child
  798.     // elements.
  799.  
  800.     if((_strcmpi(sOutputType, in.m_sOutputType) == 0) ||
  801.        (_strcmpi(sOutputType, "both") == 0 && 
  802.         (_strcmpi(in.m_sOutputType, "doc") == 0 || _strcmpi(in.m_sOutputType, "help") == 0)
  803.        )
  804.       )
  805.     {
  806.         m_nState.Tag = TRUE;
  807.         m_nState.Skip = FALSE;
  808.     }
  809.     else
  810.     {
  811.         m_nState.Tag = FALSE;
  812.         m_nState.Skip = TRUE;
  813.     }
  814.  
  815.     return m_nState.Tag;
  816. }
  817.  
  818.  
  819.